/*
 * @(#)ThinkGearStreamParser.c    2.0    Mar 04, 2008
 *
 * Copyright (c) 2008 NeuroSky, Inc. All Rights Reserved
 * NEUROSKY PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
Copyright (c) 2010 Embedded Systems and Pervasive Laboratory, Federal
University of Campina Grande, Brazil, Angelo Perkusich, Mirko Perkusich,
Taciana Rached

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN  THE SOFTWARE.
*/
/**
 * @file ThinkGearStreamParser.c
 *
 * @author Kelvin Soo
 * @version 2.0 Mar 04, 2008 Kelvin Soo
 *   - Renamed to ThinkGearStreamParser from ThinkGearStreamDecoder.
 *   - Revised to call a callback function instead of stuffing arriving
 *     data values into a ThinkGearData object.
 *   - Renamed symbols according to updated Packet vocabulary.
 * @version 1.0 Nov 27, 2007 Kelvin Soo
 *   - Initial version (ThinkGearStreamDecoder).
 */

#include "ThinkGearStreamParser.h"

/* Include libraries required specifically by this implementation of this
 * library and not already included by this library's header
 */
#include <stdio.h>

/* Decoder states (Packet decoding) */
#define PARSER_STATE_NULL           0x00  /* NULL state */
#define PARSER_STATE_SYNC           0x01  /* Waiting for SYNC byte */
#define PARSER_STATE_SYNC_CHECK     0x02  /* Waiting for second SYNC byte */
#define PARSER_STATE_PAYLOAD_LENGTH 0x03  /* Waiting for payload[] length */
#define PARSER_STATE_PAYLOAD        0x04  /* Waiting for next payload[] byte */
#define PARSER_STATE_CHKSUM         0x05  /* Waiting for chksum byte */

/* Decoder states (2-byte raw decoding) */
#define PARSER_STATE_WAIT_HIGH      0x06  /* Waiting for high byte */
#define PARSER_STATE_WAIT_LOW       0x07  /* High r'cvd.  Expecting low part */

/* Other constants */
#define PARSER_SYNC_BYTE            0xAA  /* Syncronization byte */
#define PARSER_EXCODE_BYTE          0x55  /* EXtended CODE level byte */

/*
 * See header file for interface documentation.
 */
ThinkGearStreamParser::ThinkGearStreamParser()
{
    int parserType = PARSER_TYPE_PACKETS;
    /* Initialize the parser's state based on the parser type */
    switch( parserType ) {
        case( PARSER_TYPE_PACKETS ):
            state = PARSER_STATE_SYNC;
            break;
        case( PARSER_TYPE_2BYTERAW ):
            state = PARSER_STATE_WAIT_HIGH;
            break;
    }

    /* Save parser type */
    type = parserType;
}

/*
 * See header file for interface documentation.
 */
int ThinkGearStreamParser::parseByte( unsigned char byte ) {

    int returnValue = 0;

    /* Pick handling according to current state... */
    switch( state ) {

        /* Waiting for SyncByte */
        case( PARSER_STATE_SYNC ):
            if( byte == PARSER_SYNC_BYTE ) {
                state = PARSER_STATE_SYNC_CHECK;
            }
            break;

        /* Waiting for second SyncByte */
        case( PARSER_STATE_SYNC_CHECK ):
            if( byte == PARSER_SYNC_BYTE ) {
                state = PARSER_STATE_PAYLOAD_LENGTH;
            } else {
                state = PARSER_STATE_SYNC;
            }
            break;

        /* Waiting for Data[] length */
        case( PARSER_STATE_PAYLOAD_LENGTH ):
            payloadLength = byte;
            if( payloadLength > 170 ) {
                state = PARSER_STATE_SYNC;
                returnValue = -3;
            } else if( payloadLength == 170 ) {
                returnValue = -4;
            } else {
                payloadBytesReceived = 0;
                payloadSum = 0;
                state = PARSER_STATE_PAYLOAD;
            }
            break;

        /* Waiting for Payload[] bytes */
        case( PARSER_STATE_PAYLOAD ):
            payload[payloadBytesReceived++] = byte;
            payloadSum = (unsigned char)(payloadSum + byte);
            if( payloadBytesReceived >= payloadLength ) {
                state = PARSER_STATE_CHKSUM;
            }
            break;

        /* Waiting for CKSUM byte */
        case( PARSER_STATE_CHKSUM ):
            chksum = byte;
            state = PARSER_STATE_SYNC;
            if( chksum != ((~payloadSum)&0xFF) ) {
                returnValue = -2;
            } else {
                returnValue = 1;
                parsePacketPayload();
            }
            break;

        /* unrecognized state */
        default:
            state = PARSER_STATE_SYNC;
            returnValue = -5;
            break;
    }

    /* Save current byte */
    lastByte = byte;

    return( returnValue );
}

/**
 * Parses each row of data from the @c packet's Data[] block,
 * updating the fields of @c data as appropriate.
 */
int ThinkGearStreamParser::parsePacketPayload() {

    unsigned char i = 0;
    unsigned char extendedCodeLevel = 0;
    unsigned char code = 0;
    unsigned char numBytes = 0;

    /* Parse all bytes from the payload[] */
    while( i < payloadLength ) {

        /* Parse possible EXtended CODE bytes */
        while( payload[i] == PARSER_EXCODE_BYTE ) {
            extendedCodeLevel++;
            i++;
        }

        /* Parse CODE */
        code = payload[i++];

        /* Parse value length */
        if( code >= 0x80 ) numBytes = payload[i++];
        else               numBytes = 1;

        /* Call the callback function to handle the DataRow value */
        ThinkGearStreamParser::handleDataValue( extendedCodeLevel, code, numBytes,
                                     payload+i);
        i = (unsigned char)(i + numBytes);
    }

    return( 0 );
}

void ThinkGearStreamParser::handleDataValue(unsigned char extendedCodeLevel,
                         unsigned char code,
                         unsigned char valueLength,
                         const unsigned char *value)
{

    if( extendedCodeLevel == 0 ) {
        switch( code ) {
            /* [CODE]: ATTENTION eSense */
            case( 0x04 ):
                qDebug() << "Attention Level: " << (value[0] & 0xFF);
                emit attentionLevel(value[0] & 0xFF);
                break;
            /* [CODE]: MEDITATION eSense */
            case( 0x05 ):
                qDebug() << "Meditation Level: " << (value[0] & 0xFF);
                emit meditationLevel(value[0] & 0xFF);
                break;
            /* Other [CODE]s */
            default:
               qDebug() << "No valid data";
        }
    }
}
